/*
 * Copyright 2011 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.mikgonz.farkle;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;

import com.google.web.bindery.event.shared.SimpleEventBus;
import com.google.web.bindery.requestfactory.shared.RequestFactory;
import com.google.web.bindery.requestfactory.vm.RequestFactorySource;
import com.mikgonz.farkle.shared.UserProxy;

/**
 * Utility methods for getting the base URL for client-server communication and
 * retrieving shared preferences.
 */
public final class Util {

	public static final int NOTIF_ID = 3;

	/**
	 * Tag for logging.
	 */
	private static final String TAG = "Util";

	// Shared constants

	/**
	 * Key for account name in shared preferences.
	 */
	public static final String ACCOUNT_NAME = "accountName";

	/**
	 * Key for auth cookie name in shared preferences.
	 */
	public static final String AUTH_COOKIE = "authCookie";

	/**
	 * Key for device registration id in shared preferences.
	 */
	public static final String DEVICE_REGISTRATION_ID = "deviceRegistrationID";

	/*
	 * URL suffix for the RequestFactory servlet.
	 */
	public static final String RF_METHOD = "/gwtRequest";

	/**
	 * An intent name for receiving registration/unregistration status.
	 */
	public static final String UPDATE_UI_INTENT = getPackageName()
			+ ".UPDATE_UI";

	// End shared constants

	/**
	 * Key for shared preferences.
	 */
	private static final String SHARED_PREFS = "farklegame"
			.toUpperCase(Locale.ENGLISH) + "_PREFS";

	/**
	 * Cache containing the base URL for a given context.
	 */
	private static final Map<Context, String> URL_MAP = new HashMap<Context, String>();

	public static final String PREF_USERNAME = "username";

	public static final String PREF_PASSWORD = "password";

	public static final String PREF_USERID = "userid";

	public static final String PREF_XTIFYKEY = "xtifykey";

	/**
	 * Display a notification containing the given string.
	 */
	public static void generateNotification(Context context, String message) {
		int icon = R.drawable.status_icon;
		long when = System.currentTimeMillis();

		Notification notification = new Notification(icon, message, when);
		notification.setLatestEventInfo(context, "C2DM Example", message,
				PendingIntent.getActivity(context, 0, null,
						PendingIntent.FLAG_CANCEL_CURRENT));
		notification.flags |= Notification.FLAG_AUTO_CANCEL;

		SharedPreferences settings = Util.getSharedPreferences(context);
		int notificatonID = settings.getInt("notificationID", 0);

		NotificationManager nm = (NotificationManager) context
				.getSystemService(Context.NOTIFICATION_SERVICE);
		nm.notify(notificatonID, notification);

		SharedPreferences.Editor editor = settings.edit();
		editor.putInt("notificationID", ++notificatonID % 32);
		editor.commit();
	}

	/**
	 * Returns the (debug or production) URL associated with the registration
	 * service.
	 */
	public static String getBaseUrl(Context context) {
		String url = URL_MAP.get(context);
		if (url == null) {
			// if a debug_url raw resource exists, use its contents as the url
			url = getDebugUrl(context);
			// otherwise, use the production url
			if (url == null) {
				url = Setup.PROD_URL;
			}
			URL_MAP.put(context, url);
		}
		return url;
	}

	/**
	 * Creates and returns an initialized {@link RequestFactory} of the given
	 * type.
	 */
	public static <T extends RequestFactory> T getRequestFactory(
			Context context, Class<T> factoryClass) {
		T requestFactory = RequestFactorySource.create(factoryClass);

		SharedPreferences prefs = getSharedPreferences(context);
		String authCookie = prefs.getString(Util.AUTH_COOKIE, null);

		String uriString = Util.getBaseUrl(context) + RF_METHOD;
		URI uri;
		try {
			uri = new URI(uriString);
		} catch (URISyntaxException e) {
			Log.w(TAG, "Bad URI: " + uriString, e);
			return null;
		}
		requestFactory.initialize(new SimpleEventBus(),
				new AndroidRequestTransport(uri, authCookie));

		return requestFactory;
	}

	/**
	 * Helper method to get a SharedPreferences instance.
	 */
	public static SharedPreferences getSharedPreferences(Context context) {
		return context.getSharedPreferences(SHARED_PREFS, 0);
	}

	/**
	 * Returns true if we are running against a dev mode appengine instance.
	 */
	public static boolean isDebug(Context context) {
		// Although this is a bit roundabout, it has the nice side effect
		// of caching the result.
		return !Setup.PROD_URL.equals(getBaseUrl(context));
	}

	/**
	 * Returns a debug url, or null. To set the url, create a file
	 * {@code assets/debugging_prefs.properties} with a line of the form
	 * 'url=http:/<ip address>:<port>'. A numeric IP address may be required in
	 * situations where the device or emulator will not be able to resolve the
	 * hostname for the dev mode server.
	 */
	private static String getDebugUrl(Context context) {
		BufferedReader reader = null;
		String url = null;
		try {
			AssetManager assetManager = context.getAssets();
			InputStream is = assetManager.open("debugging_prefs.properties");
			reader = new BufferedReader(new InputStreamReader(is));
			while (true) {
				String s = reader.readLine();
				if (s == null) {
					break;
				}
				if (s.startsWith("url=")) {
					url = s.substring(4).trim();
					break;
				}
			}
		} catch (FileNotFoundException e) {
			// O.K., we will use the production server
			return null;
		} catch (Exception e) {
			Log.w(TAG, "Got exception " + e);
			Log.w(TAG, Log.getStackTraceString(e));
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					Log.w(TAG, "Got exception " + e);
					Log.w(TAG, Log.getStackTraceString(e));
				}
			}
		}

		return url;
	}

	/**
	 * Returns the package name of this class.
	 */
	private static String getPackageName() {
		return Util.class.getPackage().getName();
	}

	public static int identifyView(View v) {
		int imgIndex;

		switch (v.getId()) {
		case (R.id.img_1):
			imgIndex = 0;
			break;
		case (R.id.img_2):
			imgIndex = 1;
			break;
		case (R.id.img_3):
			imgIndex = 2;
			break;
		case (R.id.img_4):
			imgIndex = 3;
			break;
		case (R.id.img_5):
			imgIndex = 4;
			break;
		case (R.id.img_6):
			imgIndex = 5;
			break;
		default:
			Log.w("DieManager.IdentifyView", "Could not identify view");
			return -1;
		}

		return imgIndex;
	}

	public static AlertDialog buildAlertDialog(Context c, String title,
			String posText, DialogInterface.OnClickListener posListener,
			String negText, DialogInterface.OnClickListener negListener) {

		AlertDialog.Builder builder = new AlertDialog.Builder(c);
		builder.setCancelable(false);

		builder.setMessage(title);
		builder.setCancelable(true);

		builder.setNegativeButton(negText, negListener);
		builder.setPositiveButton(posText, posListener);

		return builder.create();
	}

	public static void createAndNotify(Context c, int tickerId,
			PendingIntent contentIntent, int titleId, int detailsId) {

		Resources r = c.getResources();

		NotificationManager nm = (NotificationManager) c
				.getSystemService(Context.NOTIFICATION_SERVICE);

		Notification notification = new Notification(R.drawable.chat,
				r.getString(tickerId), System.currentTimeMillis());

		notification.flags = Notification.FLAG_AUTO_CANCEL;

		notification.setLatestEventInfo(c, r.getString(titleId),
				r.getString(detailsId), contentIntent);

		nm.notify(NOTIF_ID, notification);
	}

	public static void checkSession(Context c, OnlineSessionListener l) {
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(c);

		final String xtifyKey = prefs.getString(PREF_XTIFYKEY, null);
		Log.w("Util", "xtifyKey == null: " + (xtifyKey == null));
		final String userName = prefs.getString(PREF_USERNAME, null);
		Log.w("Util", "userName == null: " + (userName == null));
		final String userId = prefs.getString(PREF_USERID, null);
		Log.w("Util", "userId == null: " + (userId == null));
		final String password = prefs.getString(PREF_PASSWORD, null);
		Log.w("Util", "password == null: " + (password == null));

		if (xtifyKey == null || userName == null || userId == null
				|| password == null) {
			l.onSessionDead();
			return;
		}

		class NewTask extends AsyncRPC {

			private OnlineSessionListener l;

			public NewTask(Context c, OnlineSessionListener l) {
				super(c);
				this.l = l;
			}

			@Override
			protected void onPostExecute(Object result) {
				UserProxy user = (UserProxy) result;

				if (user == null)
					l.onSessionDead();
				else
					l.onSessionLive(user);

			}

		}

		NewTask task = new NewTask(c, l);
		task.execute(AsyncRPC.LOGIN, userName, password, userId, xtifyKey);
	}

	public final static String TO_LISTEN = "toListen";

	public static ProgressDialog getProgressDialog(Context c, int messsageId,
			OnCancelListener l) {

		final ProgressDialog p = new ProgressDialog(c);
		p.setIndeterminate(true);
		p.setCancelable(false);
		p.setMessage(c.getString(messsageId));
		if (l != null) {
			p.setOnCancelListener(l);
			p.setCancelable(true);
		}

		return p;
	}

}
